/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * SequenceFrame.java
 *
 * Created on Apr 14, 2010, 4:03:22 PM
 */
package thetaeditor;

import java.io.File;
import javax.swing.JScrollPane;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import java.awt.event.*;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileFilter;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
//import java.awt.image.*;

/**
 *
 * @author garrett
 */
public class SequenceFrame extends JFrame {

    private Sequence editing = null;

    public SequenceFrame() {
        this(null);
    }

    /** Creates new form SequenceFrame */
    public SequenceFrame(Sequence editing) {
        initComponents();
        frameOrganizer = new FrameOrganizer();
        framesPanel.setLayout(new java.awt.FlowLayout());
        JScrollPane organizeScroll = new JScrollPane(frameOrganizer);
        framesPanel.add(organizeScroll);

        if (editing == null) {
            editing = new Sequence();
        }
        this.editing = editing;
        for (String f : editing.frames) {
            frameOrganizer.addImage(f);
        }

        SpinEar se = new SpinEar();
        fpsSpinner.addChangeListener(se);
        durationSpinner.addChangeListener(se);

        Listener l = new Listener();
        importButton.addActionListener(l);
        playButton.addActionListener(l);
        rewindButton.addActionListener(l);
        endButton.addActionListener(l);

        flipXCheck.addActionListener(l);
        flipYCheck.addActionListener(l);
        loopCheck.addActionListener(l);

        okayButton.addActionListener(l);
        cancelButton.addActionListener(l);
        
        flipXCheck.setSelected(editing.flipX);
        flipYCheck.setSelected(editing.flipY);
        loopCheck.setSelected(editing.loop);
        durationSpinner.setValue(editing.duration);

        frameOrganizer.setFlipX(editing.flipX);
        frameOrganizer.setFlipY(editing.flipY);
        

        pack();
        organizeScroll.setPreferredSize(new java.awt.Dimension(476, 170));

        SequencePlayer sp = new SequencePlayer();
        sp.start();
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        previewButton = new javax.swing.JButton();
        importButton = new javax.swing.JButton();
        flipXCheck = new javax.swing.JCheckBox();
        flipYCheck = new javax.swing.JCheckBox();
        loopCheck = new javax.swing.JCheckBox();
        playButton = new javax.swing.JButton();
        endButton = new javax.swing.JButton();
        jLabel1 = new javax.swing.JLabel();
        rewindButton = new javax.swing.JButton();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        durationSpinner = new javax.swing.JSpinner();
        jLabel4 = new javax.swing.JLabel();
        fpsSpinner = new javax.swing.JSpinner();
        okayButton = new javax.swing.JButton();
        cancelButton = new javax.swing.JButton();
        framesPanel = new javax.swing.JPanel();

        setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
        setTitle("Sequence");
        setResizable(false);

        importButton.setText("Import Frames");

        flipXCheck.setText("Flip-X");

        flipYCheck.setText("Flip-Y");

        loopCheck.setText("Loop");

        playButton.setText(">");
        playButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                playButtonActionPerformed(evt);
            }
        });

        endButton.setText(">|");

        jLabel1.setText("Preview");

        rewindButton.setText("|<");

        jLabel2.setText("Sequence Frames");

        jLabel3.setText("Duration");

        durationSpinner.setModel(new javax.swing.SpinnerNumberModel(Double.valueOf(0.0d), Double.valueOf(0.0d), null, Double.valueOf(1.0d)));

        jLabel4.setText("FPS");

        fpsSpinner.setModel(new javax.swing.SpinnerNumberModel(Double.valueOf(0.0d), Double.valueOf(0.0d), null, Double.valueOf(1.0d)));

        okayButton.setText("Okay");

        cancelButton.setText("Cancel");

        javax.swing.GroupLayout framesPanelLayout = new javax.swing.GroupLayout(framesPanel);
        framesPanel.setLayout(framesPanelLayout);
        framesPanelLayout.setHorizontalGroup(
            framesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 476, Short.MAX_VALUE)
        );
        framesPanelLayout.setVerticalGroup(
            framesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 167, Short.MAX_VALUE)
        );

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addGroup(layout.createSequentialGroup()
                                        .addComponent(importButton)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(flipXCheck)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(flipYCheck)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(loopCheck))
                                    .addGroup(layout.createSequentialGroup()
                                        .addComponent(jLabel3)
                                        .addGap(1, 1, 1)
                                        .addComponent(durationSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(jLabel4)
                                        .addGap(1, 1, 1)
                                        .addComponent(fpsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 75, javax.swing.GroupLayout.PREFERRED_SIZE)))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(okayButton)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(cancelButton))
                            .addComponent(framesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(previewButton, javax.swing.GroupLayout.PREFERRED_SIZE, 183, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addGroup(layout.createSequentialGroup()
                                .addComponent(rewindButton)
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                .addComponent(playButton)
                                .addGap(6, 6, 6)
                                .addComponent(endButton))))
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jLabel2)
                        .addGap(345, 345, 345)
                        .addComponent(jLabel1)))
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jLabel1)
                    .addComponent(jLabel2))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(previewButton, javax.swing.GroupLayout.DEFAULT_SIZE, 167, Short.MAX_VALUE)
                    .addComponent(framesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(importButton)
                            .addComponent(flipXCheck)
                            .addComponent(flipYCheck)
                            .addComponent(loopCheck))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jLabel3)
                            .addComponent(durationSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(jLabel4)
                            .addComponent(fpsSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(okayButton)
                            .addComponent(cancelButton)))
                    .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                        .addComponent(rewindButton)
                        .addComponent(playButton)
                        .addComponent(endButton))))
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents

    private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed
        // TODO add your handling code here:
    }//GEN-LAST:event_playButtonActionPerformed

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                SequenceFrame f = new SequenceFrame();
                f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                f.setVisible(true);
            }
        });
    }
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton cancelButton;
    private javax.swing.JSpinner durationSpinner;
    private javax.swing.JButton endButton;
    private javax.swing.JCheckBox flipXCheck;
    private javax.swing.JCheckBox flipYCheck;
    private javax.swing.JSpinner fpsSpinner;
    private javax.swing.JPanel framesPanel;
    private javax.swing.JButton importButton;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JCheckBox loopCheck;
    private javax.swing.JButton okayButton;
    private javax.swing.JButton playButton;
    private javax.swing.JButton previewButton;
    private javax.swing.JButton rewindButton;
    // End of variables declaration//GEN-END:variables
    private FrameOrganizer frameOrganizer;

    public Sequence getSequence() {
        Sequence seq = new Sequence();
        seq.duration = (Double) durationSpinner.getValue();
        seq.loop = loopCheck.isSelected();
        seq.flipX = flipXCheck.isSelected();
        seq.flipY = flipYCheck.isSelected();
        seq.frames = frameOrganizer.getFramePaths();
        return seq;
    }

    private void playPreview() {
        play = true;
        synchronized (playerLock) {
            playerLock.notify();
        }
    }

    private void pausePreview() {
        play = false;
    }

    private void stopPreview() {
        play = false;
        secsIn = 0;
        if (frameOrganizer.numFrames() > 0)
            previewButton.setIcon(new ImageIcon(
                    frameOrganizer.getThumb(0)));
        else
            previewButton.setIcon(null);
    }

    private void forwardPreview() {
        play = false;
        secsIn = 0;
        if (frameOrganizer.numFrames() > 0)
            previewButton.setIcon(new ImageIcon(
                    frameOrganizer.getThumb(frameOrganizer.numFrames()-1)));
        else
            previewButton.setIcon(null);
    }

    private final Object playerLock = new Object();
    private boolean play = false;
    private double secsIn = 0;

    class SequencePlayer extends Thread {

        @Override
        public void run() {
            long timeStart = 0;
            long timeEnd = 0;
            long timePass = 0;
            double secPass = 0;

            while (true) {
                while (!play) {
                    playButton.setText(">");
                    synchronized (playerLock) {
                        try {
                            playerLock.wait();
                        } catch (InterruptedException e) {
                        }
                    }
                    timeStart = System.currentTimeMillis();
                    secsIn = 0;
                }
                playButton.setText("||");

                if (play) {
                    timeEnd = System.currentTimeMillis();
                    timePass = timeEnd - timeStart;
                    secPass = timePass / 1000.;
                    timeStart = System.currentTimeMillis();

                    secsIn += secPass;

                    double duration = (Double) durationSpinner.getValue();
                    if (duration <= 0) {
                        play = false;
                        continue;
                    }

                    int frames = frameOrganizer.numFrames();
                    if (frames == 0) {
                        play = false;
                        continue;
                    }

                    int fi = (int) (frames * secsIn / duration);
                    if (fi >= frames && loopCheck.isSelected()) {
                        fi = fi % frames;
                    }
                    if (fi < frames) {
                        previewButton.setIcon(new ImageIcon(
                                frameOrganizer.getThumb(fi)));
                    } else {
                        play = false; // stop now.
                    }
                }

                try {
                    sleep(10);
                } catch (InterruptedException e) {
                }
            }
        }
    }

    class ImgFilter extends FileFilter {

        @Override
        public boolean accept(File f) {
            return f.isDirectory()
                    || f.getName().toLowerCase()
                    .matches(".*?\\.(png)|(gif)|(jpeg)|(jpg)");
        }

        @Override
        public String getDescription() {
            return "Image Files (png, gif, jpg, jpeg)";
        }
    }

    class Listener implements ActionListener {

        JFileChooser fc;

        public void actionPerformed(ActionEvent e) {
            if (e.getSource() == importButton) {
                if (fc == null) {
                    fc = new JFileChooser();
                    fc.setFileFilter(new ImgFilter());
                    fc.setMultiSelectionEnabled(true);
                    fc.setAcceptAllFileFilterUsed(false);
                }
                int approve = fc.showOpenDialog(framesPanel);
                if (approve != JFileChooser.APPROVE_OPTION) {
                    return;
                }
                File[] files = fc.getSelectedFiles();
                for (File f : files) {
                    if (f.isDirectory()) {
                        continue;
                    }
                    frameOrganizer.addImage(f);
                }
            } else if (e.getSource() == playButton) {
                if (!play) {
                    playPreview();
                } else {
                    pausePreview();
                }
            } else if (e.getSource() == rewindButton) {
                stopPreview();
            } else if (e.getSource() == endButton) {
                forwardPreview();
            } else if (e.getSource() == flipXCheck) {
                frameOrganizer.setFlipX(flipXCheck.isSelected());
            } else if (e.getSource() == flipYCheck) {
                frameOrganizer.setFlipY(flipYCheck.isSelected());
            } else if (e.getSource() == okayButton) {
                editing.read(getSequence().store());
                setVisible(false);
                dispose();
            } else if (e.getSource() == cancelButton) {
                setVisible(false);
                dispose();
            }
        }
    }

    class SpinEar implements ChangeListener {

        private int ignore = 0;

        public void stateChanged(ChangeEvent e) {
            if (e.getSource() == fpsSpinner) {
                //System.out.println("Fps-Ignore: " + ignore);
                double d = (Double) fpsSpinner.getValue();
                if (d < 0.001) {
                    fpsSpinner.setValue(0.001);
                    return;
                }
                if (ignore != 1) {
                    ignore = 2;
                    durationSpinner.setValue(frameOrganizer.numFrames() / d);
                } else {
                    ignore = 0;
                }
            } else if (e.getSource() == durationSpinner) {
                //System.out.println("Duration-Ignore: " + ignore);
                double d = (Double) durationSpinner.getValue();
                if (d < 0.001) {
                    durationSpinner.setValue(0.001);
                    return;
                }
                if (ignore != 2) {
                    ignore = 1;
                    fpsSpinner.setValue(frameOrganizer.numFrames() / d);
                } else {
                    ignore = 0;
                }
            }
        }
    }
}
